
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>		// for strlen
#include "tilenpc.h"




#define INFOSCREEN_LINECOUNT 17
const char *info_screen_lines[INFOSCREEN_LINECOUNT] = {
    "Matchem Poker (v.0.9)",
    "A Nokia example",
    "",
    "Ported from iOS",
    "using Qt GameEnabler",
    "projects.forum.nokia",
    "/qtgameenabler",
    "",
    "Simply switch cards",
    "with eachother to",
    "create poker hands.",
    "",
    "Audio samples from",
    "FREESOUND.ORG used.",
    "Thanks to:",
    "milton, mattwasser,",
    "Christianjinnyzoe"
};


/*


 !"#$% �()*+,-./
0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]^_
`abcdefghijklmno
pqrstuvwxyz{|}~

*/
const char *happy_strings[] = {
    "Playing cards were most likely invented in China in 1120 AD and first introduced to Europe in the 1300s. ",
    "Due to French influence, Spades represent Royalty, Diamonds represent Merchants, Clubs represent Peasants, and Hearts represent the Clergy.",
    "A fifth suit was added in 1937 but never caught on because people had to buy all new decks.",
    "When Columbus landed on U.S. shores in 1492, his men plucked wide leaves from trees, marked them with images, and played cards. ",
    "There are 1,326 possible 2-card combinations a player can start with in Texas Hold�em.",
    "There is a 1 in 16 chance of being dealt pocket pairs.",
    "There is a 1 in 8.3 chance of flopping a set after being dealt pocket pairs."
    "Poker is�a fascinating, wonderful, intricate adventure on the high seas of human nature.",
    "40 to 50 million Americans regularly play poker. That�s more than one in five� ",
    "Each year more than 70 million decks of cards are sold in the U.S.",
    "Make a Waldorf salad, with apples, celery, walnuts, and dressing.",
    "\"Patience, and shuffle the cards.\" -Cervantes, Don Quixote.",
    "The next best thing to playing and winning is playing and losing. The main thing is to play.",
    "I must complain the cards are ill shuffled till I have a good hand.",
    "POKER, n. A game said to be played with cards for some purpose to this lexicographer unknown.",
    "If one is able and strong, then one should disguise oneself in order to appear inept and weak.",
    "Perception is reality.",
    "Clubs -Alexander, the Great.",
    "Spades - King David.",
    "It�s not enough to succeed. Others must fail.",
    "Diamonds - Julius Caesar.",
    "There are two \"one-eyed\" Jacks in a deck of cards. The Jack of Spades and the Jack of Hearts.",
    "A \"Dead man's hand\" is Aces and Eights.",
    "Hearts - Charlemagne.",
    "No one knows the true origins of poker.",
    "I'm getting tired of poker now...",
    "Don't play if you need to win.",
    "Sound effects are all from FreeSound.org from following tags: milton, mattwasser, and Christianjinnyzoe.",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "...",
    "Warning, End of the list.. ",
    };




    // score roll
const int rollWidth = 10000;
const int rollSpacing = 6500;
const int scoreRollStart = 33000;
const int scoreRollXPos[] = { 6500,6500 + rollSpacing, scoreRollStart,scoreRollStart+rollSpacing,scoreRollStart+rollSpacing*2,scoreRollStart+rollSpacing*3,scoreRollStart+rollSpacing*4 };



GameTexture gameTextures[]=
{
{ TexLogo, "logo",1,1},
{ TexPieces, "playing_cards", 4,5},
{ TexPiecesSelected, "playing_cards_selected", 4, 5},
{ TexFontScore, "scorefont",10,1},
{ TexGradient, "gradient"},
{ TexMeter, "timer_numbers",4,5},
{ TexMeterBase, "timer_frames",1,2},
{ TexParticle, "particles",2,2},
{ TexFont, "font", 16,6},
{ TexBackground, "menu_bg", 2,2,},
{ TexExtra1, "dice_pause_play", 2, 1},
{ TexEndOfList }
};



ITileGame *createTileGame( ITileRenderer *renderer, int x, int y, int width, int height  ) {
	return new CTileNpc( renderer, x,y,width,height );	
};

CTileNpc::CTileNpc( ITileRenderer *renderer, int x,int y, int width, int height  ) : ITileGame( renderer,x,y,width,height ) {
	m_level = new CNpcLevel( renderer,m_pengine, TILE_GRIDWIDTH, TILE_GRIDHEIGHT );


        m_infoScreenDisplayCounter = -1;
	m_effectAngle = 0;
        m_bgIndex1 = 1;
        m_bgIndex2 = 0;
	m_fadingBgCounter = -1;
	m_menuCounter = -1;
	m_menuModeCounter = -1;

	m_timeSinceLastScore = 0;
	m_gameIsOn = 0;
	m_timer = 0;
	m_gameOverCounter = -1;
	m_pauseCounter = -1;
	m_levelCompletedPoem = "NOT_SET";
	m_timeTimerEffect = 65535;
	m_completedTextAngle = 0;
	m_completedTextCounter = -65536*21;

	m_eventCounter = 0;
	m_bgAngle = 0;
	m_logoWobble = 0;
	m_logoWobbleInc = 0;

	m_currentLevel = 0;
	m_blockTimerEffect = 0;
	m_targetTimer= 0;


	m_levelCompletedCounter = 0;
	m_highScore = 0;
	m_timer = 0;
	m_score = 0;
        m_displayScore =0;
	m_difficulty = 1;			// adults, 0 for kids ?
        m_level->setGameArea(x-0000,y-8000,width+0000,height+6000 );
        //m_level->setGameArea( x,y, width, height );

        memset( m_scoreRollPos, 0, sizeof(int) *7  );
        memset( m_scoreRollTargetPos, 0, sizeof(int) *7  );
        memset( m_scoreRollExTargetPos, 0, sizeof(int) *7  );
        memset( m_scoreRollInc, 0, sizeof(int) *7  );

};


CTileNpc::~CTileNpc() {
	save();
	if (m_level) {
		delete m_level;
		m_level = 0;
	};
	if (m_pengine) {
		delete m_pengine;
		m_pengine = 0;
	};
	
};


void CTileNpc::gameStateChanged() {
        m_infoScreenDisplayCounter = -1;
	m_pauseCounter = -1;
	switch (m_state) {
		case eTILEGAMESTATE_PAUSED:
			m_completedTextCounter = -65536*21;
			m_pauseCounter = 0;
			break;	
		case eTILEGAMESTATE_GAMEOVER:
			m_completedTextCounter = -65536*21;
			m_level->setLevelState( eLEVELSTATE_GAMEOVER );
			break;
		case eTILEGAMESTATE_RUNGAME:
			if (m_gameIsOn==0) {
				nextLevel(0);
			}
			break;

		case eTILEGAMESTATE_MENU:
			changeBg(0);
			m_timeTimerEffect = 65536;
			m_logoWobble = 65535;
			m_menuCounter = 0;
			m_menuModeCounter = 0;
			m_completedTextCounter = -65536*21;
			break;

               case eTILEGAMESTATE_SHOWINFOSCREEN:
                    m_infoScreenDisplayCounter = 0;
                    break;

	};
};



void CTileNpc::gameOver() {
	m_gameOverCounter = 0;
	m_gameIsOn = 0;
	setGameState( eTILEGAMESTATE_GAMEOVER );
};


void CTileNpc::levelCompleted() {
	m_levelCompletedCounter = 0;
	m_completedTextCounter = -65536*20;
	m_levelCompletedPoem = happy_strings[ m_currentLevel % 44 ];
	m_renderer->effectNotify( eEFFECT_LEVELCOMPLETED, 0,0 );
	m_level->setLevelState( eLEVELSTATE_LEVELCOMPLETED );
	m_score += m_currentLevel*100;
        m_pengine->spray(
                    20,
                    65536/2, 20000, 1000,
                    0,-50000, 65536,
                    0, 	&m_level->m_fruitSpray );

};


void CTileNpc::endGame() {
	gameOver();
};


void CTileNpc::changeBg( int newBg ) {
	if (newBg==-1) {
                newBg = ((m_currentLevel+1)&1);
	} 
	m_bgIndex2 = newBg;
	if (m_bgIndex1==m_bgIndex2) return;		// no need
	m_fadingBgCounter = 0;		// START fading
};
	
int CTileNpc::gameRun( int fixedFrameTime ) {

        if (fixedFrameTime>8000) fixedFrameTime = 8000;

        if (m_infoScreenDisplayCounter>=0)
            m_infoScreenDisplayCounter+=fixedFrameTime;


//        if (m_state == eTILEGAMESTATE_

        m_effectAngle+=fixedFrameTime;

	if (m_fadingBgCounter>=0) {
		m_fadingBgCounter+=fixedFrameTime;
		if (m_fadingBgCounter>65535) {
			m_bgIndex1 = m_bgIndex2;
			m_bgIndex2 = 0;
			m_fadingBgCounter = -1;
		};
	};

        //if (m_state==eTILEGAMESTATE_SHOWINFOSCREEN) return 1;

	if (m_state==eTILEGAMESTATE_MENU) {
		m_menuCounter+=fixedFrameTime;
		m_menuModeCounter+=fixedFrameTime;
	} else {
		if (m_menuCounter>=0) {
			if (m_menuCounter>65536*3) m_menuCounter = 65536*3;
			m_menuCounter-=fixedFrameTime;
		}

		if (m_menuModeCounter>=0) {
			if (m_menuModeCounter>65536*2) m_menuModeCounter = 65536*2;
			m_menuModeCounter-=fixedFrameTime*2;
		}
	};		


	if ( m_gameIsOn && (m_level->getState()==eLEVELSTATE_IDLE || m_level->getState()==eLEVELSTATE_LEVELCOMPLETED) ) {
		m_levelCompletedCounter+=fixedFrameTime;


		//if (m_levelCompletedCounter<65536*3)
		
	} else {
		if (m_levelCompletedCounter>65536*3) m_levelCompletedCounter = 65536*3;
		if (m_levelCompletedCounter>-1) m_levelCompletedCounter-=fixedFrameTime*2;
	};

	m_eventCounter+=fixedFrameTime*4;
	while ( m_eventCounter>65536) {



            if (m_state==eTILEGAMESTATE_MENU) {			// RANDOM SQRUIT PARTICLES
                if ((rand()&255) < 128 ) {
                    int dx = ((rand() & 511) - 256)<<6;
                    int dy = ((rand() & 255) - 512)<<7;


                    m_pengine->spray(
                                (7+(rand()&3))*5,
                                65536/2+dx, 11000, (rand()&511)*12,
                                dx,dy, (rand()&65535)/1,
                                0, 	&m_level->m_fruitSpray );


                };
                m_eventCounter = 0;
            };



            if (m_state==eTILEGAMESTATE_RUNGAME) {

                m_timeTimerEffect-=20;                // fasten up the game.
                //qDebug() << "Tim:" << m_timeTimerEffect;

                if (m_level->getState() == eLEVELSTATE_IDLE ) {
                    for (int j=0; j<3; j++)
                        if ((rand()&255)<100)
                            m_pengine->spray( 10, 2*(rand()&65535), 20000+(rand()&255)*40, 5000, 0,0, 32000, 0, &m_level->m_sparkleSpray );
                }
                int timeLimit = 4+m_currentLevel*4;
                if (timeLimit>20) timeLimit= 20;
                if (m_level->getState() == eLEVELSTATE_NORMAL && m_timeSinceLastScore>timeLimit*65536) {
                    if ((rand()&255)<32)
                        m_level->wobbleHint();
                };
            };


            m_eventCounter-=65536;



            int d=(m_score - m_displayScore);
            d>>=1;
            if (abs(d)<1) {
                if (m_displayScore>m_score) d=-1;
                else if (m_displayScore<m_score) d = 1;
            }
            //if (abs(d)<1) m_displayScore = m_score; else m_displayScore += d;
            m_displayScore += d;

	};

        int i = (m_timeTimerEffect>>9);
	if (i<1096) i = 1096;
	m_bgAngle+=(i*fixedFrameTime)>>12;
	
	//if (-40000+m_logoState*12<32000) {
	//	m_logoWobble += (((30000-m_logoWobble)*fixedFrameTime)>>16);
	//};
	int g = ((m_logoWobble * fixedFrameTime)>>13);		// string effect
	m_logoWobbleInc-=g;
	g = ((m_logoWobbleInc * fixedFrameTime)>>14);		// slowdown
	m_logoWobbleInc -= g;
	g = ((m_logoWobbleInc * fixedFrameTime)>>13);		// add
	m_logoWobble+=g;

	//


	if (m_gameOverCounter>=0) m_gameOverCounter+=fixedFrameTime;
	if (m_pauseCounter>=0) m_pauseCounter+=fixedFrameTime;

	m_completedTextAngle += fixedFrameTime;

	if (m_state==eTILEGAMESTATE_GAMEOVER || m_state==eTILEGAMESTATE_RUNGAME) 
		m_level->run( fixedFrameTime );


	if (m_state == eTILEGAMESTATE_RUNGAME) {
		if (m_level->m_hasMoves==0) {
			// this should not happend
		};

		
		
		

		int scoreAdd = m_level->takeLevelScore();
		if (scoreAdd>0) {
			m_timeSinceLastScore = 0;
		};
		m_score+=scoreAdd;
		if (m_score>m_highScore) m_highScore = m_score;

		m_targetTimer += m_level->takeLevelProgressChange() * m_blockTimerEffect;
                        // lowpass timer..
                //m_timer += (int)( (float)(m_targetTimer-m_timer) * (float)fixedFrameTime / 8000.0f );
                m_timer = m_targetTimer;




		

		if (m_completedTextCounter>=-65536*20) {
			if (m_level->getState() != eLEVELSTATE_LEVELCOMPLETED) {			// WAIT FOR level to finish completedanimation
				int tlen = strlen(m_levelCompletedPoem);
				m_completedTextCounter += fixedFrameTime * 28;
				if (m_completedTextCounter>800*65536) m_completedTextCounter = -65536*21;		// end and stop
			}
		}
		
		
		switch (m_level->getState()) {
			case eLEVELSTATE_NORMAL:
				m_timeSinceLastScore += fixedFrameTime;
                                if (m_timeSinceLastScore>m_waitBeforeTimerStartsTime) {
                                    m_targetTimer += (int)( (float)fixedFrameTime * (float)m_timeTimerEffect / 65536.0f );
                                }

                                if (/*m_doingNothingCounter>65536 &&*/ m_level->doingNothing() == true) {

                                    if (m_targetTimer >=(65536<<8)) {
                                        //qDebug() << "levelcompleted : " << m_doingNothingCounter << "dn:" << m_level->doingNothing();
                                        levelCompleted();
                                    }
                                    if (m_targetTimer<=0) gameOver();

                                }
				break;

			case eLEVELSTATE_IDLE:	
				{
					/*
				int tlen = strlen(m_levelCompletedPoem);
				if (m_completedTextCounter>tlen*65536) {
					m_completedTextCounter = strlen( m_levelCompletedPoem )*65536;
				}
				*/
				}
				break;
			
			default:

				break;
		}

		
	}
	
        /*
        if (m_level->doingNothing() == true)
            m_doingNothingCounter+=fixedFrameTime;
        else m_doingNothingCounter = 0;
        */

        // timer
        int v = ((m_timer>>8)*100) >> 16;
        if (v>99) v = 99;
        m_scoreRollTargetPos[1] = (v%10) << 16;
        m_scoreRollTargetPos[0] = ((v/10)%10) <<16;
        //m_scoreRollTargetPos[0] = ((v/100)%10) << 16;

        m_scoreRollTargetPos[6] = (m_displayScore%10) << 16;
        m_scoreRollTargetPos[5] = ((m_displayScore/10)%10) <<16;
        m_scoreRollTargetPos[4] = ((m_displayScore/100)%10) <<16;
        m_scoreRollTargetPos[3] = ((m_displayScore/1000)%10) <<16;
        m_scoreRollTargetPos[2] = ((m_displayScore/10000)%10) <<16;


        bool rollChanged = false;

        for (int sr = 0; sr<7; sr++) {
            if (m_scoreRollTargetPos[sr]!=m_scoreRollExTargetPos[sr]) rollChanged = true;
            m_scoreRollExTargetPos[sr] = m_scoreRollTargetPos[sr];

            int delta = m_scoreRollTargetPos[sr] - m_scoreRollPos[sr];
            int negDelta = (m_scoreRollTargetPos[sr]-65536*10) - m_scoreRollPos[sr];
            int plusDelta = (m_scoreRollTargetPos[sr]+65536*10) - m_scoreRollPos[sr];

            if (abs(delta)>abs(negDelta)) delta = negDelta;
            if (abs(delta)>abs(plusDelta)) delta = plusDelta;
            m_scoreRollInc[sr] += ((((delta)>>2)*fixedFrameTime ) >> 12);
            m_scoreRollInc[sr] -= (((m_scoreRollInc[sr]>>3) * fixedFrameTime) >> 10 );
            //m_scoreRollInc[sr] -= m_scoreRollInc[sr]*2/10;
            m_scoreRollPos[sr] += m_scoreRollInc[sr];
            if (m_scoreRollPos[sr]<0) m_scoreRollPos[sr] += 65536*10;
            if (m_scoreRollPos[sr]>65536*10) m_scoreRollPos[sr] -= 65536*10;
        };


        if (rollChanged) {
            m_renderer->effectNotify( eEFFECT_SCORECHANGED, 0,0 );
        };

	
	return 1;
};


#define BGTILE_OFF 40000

int CTileNpc::gameDraw( ) {

	// background.

	if (m_fadingBgCounter==-1) 
                m_renderer->renderTile( 0,0, 65536, 100000, 0, 0, (TexBackground<<16) + m_bgIndex1, 0 );
	else {
		int a = (m_fadingBgCounter>>8);
                m_renderer->renderTile( 0,0, 65536, 100000, 0, 0, (TexBackground<<16) + m_bgIndex1 | (a<<24), 0 );
                m_renderer->renderTile(  0,0, 65536, 100000, 0, 0, (TexBackground<<16) + m_bgIndex2 | ((255-a)<<24), 0 );
	};

        m_renderer->renderTile( -BGTILE_OFF,-BGTILE_OFF, 65536+BGTILE_OFF*2, 65536+BGTILE_OFF*2, ((m_bgAngle>>2)&65535), 1, ((TexBackground<<16) + 2) | (200<<24), 0 );


	if (m_state!=eTILEGAMESTATE_MENU && m_state!=eTILEGAMESTATE_PAUSED) {				// HUD
		m_level->draw( *m_renderer );	
	}




        if (m_infoScreenDisplayCounter>=0) {
            int f;
            for (int l=0; l<INFOSCREEN_LINECOUNT; l++)
                if (info_screen_lines[l][0]!=0)
                {
                    f = 65536*2 + ((l)*32000) - m_infoScreenDisplayCounter * 2;
                    if (f<0) f = 0;
                    if (f>65535) f= 65535;
                    writeEffectText( 3000+l * 5500, info_screen_lines[l], 5000, f );
                }
            //return 1;
        };


	if (m_menuCounter>=0) {
		int fade = 65536*2-(m_menuCounter);
		fade/=2;
		if (fade<0) fade = 0;
                writeEffectText( 65536 - 4500, "START", 14000, fade );


                char testr[64];
                sprintf(testr, "HI %d", m_highScore);
                writeText(0,0, testr, fade>>8, 5000,3500);
#ifndef __APPLE__
                writeText(42000,0, "EXIT", fade>>8, 8000,5500 );
#endif
	};


	if (m_menuModeCounter>0) {
		int fade = 65536-(m_menuModeCounter);
		if (fade<0) fade = 0;

                writeEffectText( 65536*5/4+4000, "INFO", 8000, fade );
                /*
                char *dif_string = "KIDS";
                if (m_difficulty==1) dif_string = "NORMAL";
                writeEffectText( 65536*5/4+6000, dif_string, 10000, fade );
                */
	}





	if (m_state!=eTILEGAMESTATE_MENU) {
                if (m_levelCompletedCounter>0) {
			int fade = 65536-(m_levelCompletedCounter);
			//fade/=2;
			if (fade<0) fade = 0;

                        writeEffectText( 19000+fade, "LEVEL", 7000, fade );
                        writeEffectText( 24000+fade, "COMPLETED", 9500, fade );
		};


		if (m_pauseCounter>0) {
			int fade = 65536-(m_pauseCounter*2);
			if (fade<0) fade = 0;
                        writeEffectText( 65536/2-4000-fade, "GAME", 9500, fade );
                        writeEffectText( 65536/2+4000+fade, "PAUSED", 9500, fade );


                        writeEffectText( 65536*4/3-4000-fade, "RESUME", 5500, fade );
                        writeEffectText( 65536*4/3+4000+fade, "END", 5500, fade );
		};


		
                if (m_gameOverCounter>0 && m_infoScreenDisplayCounter<0) {
			int fade = m_gameOverCounter-40000*2;
			if (fade>65535) fade = 65535;
			if (fade>0) {
                                writeEffectText( 12000-4000-(65535-fade)/2, "TOO BAD", 5000, 65535-fade );
				fade = m_gameOverCounter-40000*3;
				if (fade>0) {
					if (fade>65535) fade = 65535;
                                        writeEffectText( 12000+4000+(65535-fade)/2, "GAME OVER", 9000, 65535-fade );
					
					fade = m_gameOverCounter-40000*4;
					if (fade>0) {
						if (fade>65535) fade = 65535;
                                                writeEffectText( 40000, "YOUR SCORE", 5000, 65535-fade );
					
						fade = m_gameOverCounter-40000*5;
						if (fade>0) {
							if (fade>65535) fade = 65535;
							char testr[12];
							sprintf(testr, "%d", m_score );
							writeEffectText( 45000, testr, 9000, 65535-fade );

							fade = m_gameOverCounter-40000*6;
							if (fade>0) {
								if (fade>65535) fade = 65535;
                                                                writeEffectText( 60000, "AT LEVEL", 5000, 65535-fade );

								fade = m_gameOverCounter-40000*7;
								if (fade>0) {
									if (fade>65535) fade = 65535;
									sprintf(testr, "%d", m_currentLevel+1 );
									writeEffectText( 65000, testr, 9000, 65535-fade );
								}
							}
						}
					}
				}
			}
		};
	}



	writeLevelCompletedString();		// must be called always.


	if (m_hudState>0) {				// hud
                // draw the meter, clocklike here
                int x = -3000;
                int w = 65536+7000; //65536/2;
                int h = 16000;

                int y = -(65536-m_hudState)/3 - 2000;


                m_renderer->renderTile(2000,y,15000,h,0,0, (TexMeterBase<<16) | 1, 0);

                m_renderer->renderTile(29000,y,35000,h,0,0, (TexMeterBase<<16) | 1, 0);





                for (int f=0; f<7; f++)
                    m_renderer->renderTile(scoreRollXPos[f] - rollWidth/2,
                                           y+h/2-(h*7/32),
                                           rollWidth,
                                           h*14/32,
                                           0,0, (TexMeter<<16), 0);


                for (int yroll = -2; yroll<=1; yroll++) {
                    for (int f=0; f<7; f++) {


                        int num = ((m_scoreRollPos[f] >> 16) - 1 - yroll ) % 10;
                        if (num<0) num  = 10+num;

                        int yofs = ((m_scoreRollPos[f]&65535)>>4) + (yroll*4096);

                        //m_level->getCosineTable()[ yofs&4096
                        int ypos = sin( (float)yofs / 4096.0f * 3.14159f/4.0f )*(float)h/3.3f;

                        //int numheight = (65536<<10) / (abs(ypos)+4096);
                        int numheight = h/2 - abs(ypos)*13/8;


                        //numheight/=3;
                        m_renderer->renderTile(scoreRollXPos[f] - rollWidth/2,
                                               y+h/2-(numheight/2) + ypos,
                                               rollWidth,
                                               numheight,
                                               0,0, (TexMeter<<16) | (1+num), 0 );


                    }
                }





                m_renderer->renderTile(x,y,w,h,0,0, (TexMeterBase<<16) | 0, 0);

                // render pausebutton


                if (m_state == eTILEGAMESTATE_PAUSED)
                    m_renderer->renderTile(x+21000,y-65536+m_hudState,h*2/3,h*2/3*m_areaHeight/m_areaWidth,0,0, (TexExtra1<<16) | 1, 0);
                else
                    m_renderer->renderTile(x+21000,y-65536+m_hudState,h*2/3,h*2/3*m_areaHeight/m_areaWidth,0,0, (TexExtra1<<16) | 0, 0);


                m_renderer->renderTile( 14000,6000-65536+m_hudState,14000,14000*m_areaWidth/m_areaHeight,0, 0, (TexParticle<<16)+0,0 );
                int n = ((m_currentLevel+1)/10);
                if (n==0) n = 10;
                m_renderer->renderTile( 14200,6200-65536+m_hudState,13000,13000*m_areaWidth/m_areaHeight,0, 0, (TexMeter<<16)+n,0 );

                m_renderer->renderTile( 20000,9000-65536+m_hudState,14000,14000*m_areaWidth/m_areaHeight,0, 0, (TexParticle<<16)+0,0 );
                n = ((m_currentLevel+1)%10);
                if (n==0) n = 10;
                m_renderer->renderTile( 20200,9200-65536+m_hudState,13000,13000*m_areaWidth/m_areaHeight,0, 0, (TexMeter<<16)+n,0 );

                /*
		char testr[16];
                sprintf(testr, "#%.2d", m_currentLevel+1 );
                writeText( 65536 - 16000, 6000,testr, (255-(m_hudState>>8)), 5000,4000 );
                */
        }

	if (m_logoState>0) {
		int mx = 65536/2;

		
                int my = (-60536 + m_logoState*2);
		
                int w = ((((65535 - m_logoWobble*2)>>2) * 1200)>>8);
                int h = ((((65535 + m_logoWobble*2)>>2) * 1200)>>8);
		int fade = 255-(m_logoState>>4);
		if (fade<0) fade = 0;
		m_renderer->renderTile( mx-w/2,my-h,w,h, 0,0, (TexLogo<<16) | (fade<<24), 0 );			
	}

	// test
        /*
        char testr[256];
        sprintf(testr, "dn: %d", m_doingNothingCounter );
        writeText( 6000,56000, testr, 0, 8000, 5000 );
        */

        m_pengine->draw();

        /*
        if (m_level->doingNothing() == true)
            writeText( 16000,56000, "TRUE", 0, 8000, 3000 );
        else
            writeText( 16000,56000, "FALSE", 0, 8000, 3000 );
            */
	return 1;
};


void CTileNpc::gameClick( int fixedx, int fixedy, eMOUSEEVENT etype ) {

        if (m_state==eTILEGAMESTATE_SHOWINFOSCREEN) {
            if (etype == eMOUSEEVENT_BUTTONDOWN) setGameState( eTILEGAMESTATE_MENU );
            return;
        };
	if (etype==eMOUSEEVENT_BUTTONDOWN) {
		switch (m_state) {
                case eTILEGAMESTATE_GAMEOVER:
			if (m_level->getState() == eLEVELSTATE_IDLE) {
				if (m_gameOverCounter<65536*6)
					m_gameOverCounter = 65536*6;
				else
					setGameState( eTILEGAMESTATE_MENU );
			}
			return;

		case eTILEGAMESTATE_PAUSED:
			if (fixedy>83000) {
				if (fixedy>90000) gameOver(); else setGameState( eTILEGAMESTATE_RUNGAME );
			} //else m_pengine->spray( 20, fixedx,fixedy, 5000, 0,0, 32000, 0, &m_level->m_destroySpray );
			return;

		case eTILEGAMESTATE_MENU:
			//m_pengine->spray( 20, fixedx,fixedy, 5000, 0,0, 32000, 0, &m_level->m_destroySpray );

                        if (fixedx>42000 && fixedy<10000) {
#ifndef __APPLE__
                            m_renderer->effectNotify(eEFFECT_EXIT,0,0);
#endif                            
                        } else
			if (fixedy>84000) {			// mode change
                            /*
				if (m_difficulty==0) m_difficulty=1; else m_difficulty = 0;
				m_level->m_difficulty = m_difficulty;
				m_menuModeCounter = 0;
                                */

                                //showInfoScreen();
                                setGameState(eTILEGAMESTATE_SHOWINFOSCREEN);
			} else {

                                if (fixedy<60000) m_logoWobbleInc+=16000;
				else {
					setGameState( eTILEGAMESTATE_RUNGAME );
					m_logoWobble = -20000;
				}
			}
			return;

		default:
			if (m_level->getState() == eLEVELSTATE_IDLE) {			// level completed
				nextLevel();
			} else {
				//if (m_level->getState() == eLEVELSTATE_NORMAL)
				
                                if (fixedy<12000) {
                                    if (fixedx>21000 && fixedx<28000)
                                        setGameState( eTILEGAMESTATE_PAUSED );
                                    else return;
				} else {
					//levelCompleted();
					//
					//gameOver();
				}
			}
			break;
		};
	}		// end of if eMOUSEEVENT_DOWN


	if (m_gameIsOn && m_state==eTILEGAMESTATE_RUNGAME && m_level->getState()!=eLEVELSTATE_IDLE) {
		switch (etype) {
			case eMOUSEEVENT_BUTTONDOWN: m_level->click( fixedx, fixedy, 0 ); break;
			case eMOUSEEVENT_MOUSEDRAG: m_level->click( fixedx, fixedy, 1 ); break;
			case eMOUSEEVENT_BUTTONUP: m_level->click( fixedx, fixedy, 2 ); break;
		}
	}
};


void CTileNpc::nextLevel( int restartAt ) {
	
	if (restartAt>=0) {
		m_currentLevel = restartAt;
		m_score = 0;
                m_displayScore =0;
		m_gameIsOn = 1;				// mark game ongoing
		m_gameOverCounter = -1;
	} else {
		m_currentLevel++;
                m_score+=m_currentLevel*m_currentLevel*10;
	}

	m_timeSinceLastScore = 0;
	m_completedTextCounter = -21*65536;
	m_targetTimer = 65536*100;
	
	

	float temp = 5.0f / ((float)m_currentLevel+6.0f);
	m_timeTimerEffect = -(int)( 240000.0f*(1.0f-temp));
	m_timeTimerEffect -= m_currentLevel*13000;						// linear component
        m_blockTimerEffect = (65536*22) / (m_currentLevel+60);			// 30

	
        //m_waitBeforeTimerStartsTime = 65536 - m_currentLevel*5000;
        m_waitBeforeTimerStartsTime = 65536*15 / (3+m_currentLevel);
        if (m_waitBeforeTimerStartsTime<0) m_waitBeforeTimerStartsTime = 0;

	m_level->createLevel( m_currentLevel );

	changeBg();
};


void CTileNpc::setIntAttribyte( eTILEGAME_ATTRIBUTE att, int set ) {
	switch (att) {
	case eATTRIBUTE_DIFFICULTY:
		m_difficulty = set;
		m_level->m_difficulty = set;
		break;

	};

};



int CTileNpc::getIntAttribute( eTILEGAME_ATTRIBUTE att, unsigned int arg ) {
	switch (att) {
		default:
			return 0;
	case eATTRIBUTE_DIFFICULTY:
		return m_difficulty;
		break;
		case eATTRIBUTE_HIGHSCORE:
			return m_highScore;
			break;
		case eSCORE:
			return m_score;
		case eTIMELIMIT_CURRENT:
			return m_timer;
		case eTIMELIMIT_MAX:
			return 65536 * 256;
		case eATTRIBUTE_CURRENTLEVEL:
			return m_currentLevel;
			
	};
};



/*
	unsigned char m_gameState;
	int m_difficulty;
	int m_blockTimerEffect;				// kuinka paljon blokin tuhoaminen kasvattaa progressia.
	int m_timeTimerEffect;				// kuinka paljon aika hidastaa progressia
	int m_currentLevel;
	int m_targetTimer;		
	int m_timer;		
	int m_score;
	int m_highScore
*/

#ifdef __APPLE__
#define SAVE_FILE "../Documents/fcstate.bin"
#else
#define SAVE_FILE "fcstate.bin"
#endif

int CTileNpc::save() {
	FILE *file = fopen(SAVE_FILE, "wb" );
	if (!file) return 0;
        fwrite( &m_difficulty, 1,11*sizeof(int), file );
	if (m_gameIsOn==1) m_level->saveToFile( file );
	fclose(file);
	
	return 1;
};



int CTileNpc::load() {
	
	FILE *file = fopen(SAVE_FILE, "rb" );
	if (!file) return 0;
	int rval = 0;
        fread( &m_difficulty, 1,11*sizeof(int), file );
	if (m_gameIsOn) {
		m_level->loadFromFile( file );
		m_level->m_difficulty=m_difficulty;
		changeBg();
		rval = 1;
	}
	fclose(file);
	return rval;
};



void CTileNpc::writeEffectText( int y, const char *text, int charSize, int fade ) {
	char ch;

	int totcount =0;
	int *cosin = m_level->getCosineTable();
	int yadd;
	int xadd;
        int charSpace = charSize*5/8;
	int x= 65536/2 - strlen(text)*charSpace/2;
	while (*text!=0) {
		if (*text!=' ') {
                                if (ch>='a' && ch<='z') ch = ch-'a'+'A';        // to high-case
				ch = *text -32;

				yadd = 	((cosin[(totcount*850 + m_effectAngle/70)&4095]>>8)*fade)/600;
				yadd += (cosin[(1000+m_effectAngle/10+totcount*600)&4095]>>8);
				xadd = (cosin[(m_effectAngle/30+totcount*800)&4095]>>8);

				m_renderer->renderTile( x+xadd,y+yadd,
					charSize,charSize, fade*2, 0, (TexFont<<16) | ch | ((fade>>8)<<24), 0 );
				
		}
		totcount++;
		x+=charSpace;
		text++;
	};
};



void CTileNpc::writeText( int x,int y, const char *text, int fade, int charSize, int charSpace ) {
	char ch;
	while (*text!=0) {
		if (*text!=' ') {
			ch = *text-32;
			m_renderer->renderTile( x,y,charSize,charSize, 0, 0, (TexFont<<16) | ch | (fade<<24), 0 );
		}
		x+=charSpace;
		text++;
	};
};



void CTileNpc::writeLevelCompletedString() {
	
	int x = 0;
        int y = 38000;
	const char *text = m_levelCompletedPoem;
        int charSize = 6000;
        int charSpace = charSize*5/8;
	int fade = 0;

	int *cosineTable = m_level->getCosineTable();
	int totCount = 0;
	int f,g;
	char testr[128];		// Word, line.
	char cur_line[256];
	
	cur_line[0] = 0;		// end the line
	int sizeinc;
	char ch;
	while (1) {

		while ((*text==' ' || *text==10 || *text==13) && *text!=0) *text++;
		f=0;
		while (*text!=' ' && *text!=13 && *text!=10 && *text!=0 && f<255) {
			testr[f] = *text;
			*text++;
			f++;
		};
		testr[f]=0;
		
		

			// cur_line must be drawn
		if ( f==0 || (strlen(testr)+strlen(cur_line))*charSpace > 62000 ) {
			f=0;
			int j = strlen(cur_line);
			x = 65536/2-j*charSpace/2;
                        x-=charSpace/4;
			while (cur_line[f]!=0) {
                                ch = cur_line[f];
                                if (ch>='a' && ch<='z') ch = ch-'a'+'A';        // to high-case
                                ch-=32;
				g= ((totCount<<16) - m_completedTextCounter);
				//if (g<0) g = 0;
				if (g<-128*65536) g = g+128*65536; else	if (g<0) g= 0;
				
                                sizeinc = -abs(g)/256;
				fade = (g>>11);
					
				if (fade>255) return;
				fade = abs(fade);

				if (ch!=255 && fade<255)  {
                                        m_renderer->renderTile(
                                                x-sizeinc/2+(cosineTable[(1000+m_completedTextAngle/20+f*600)&4095]>>10),
                                                g/64+y-sizeinc/2+(cosineTable[(m_completedTextAngle/30+f*800)&4095]>>9),
                                                charSize+sizeinc,
                                                charSize+sizeinc,
                                                -((sizeinc*8)&65535),
                                                0,
                                                (TexFont<<16) | ch | (fade<<24),
                                                0 );
				}
				totCount++;
				x+=charSpace;
				f++;
			}

			// line drawn
			cur_line[0] = 0;		// clear it. 
			x=0;
			y+=charSpace*4/3;


			cur_line[0] = 0;
		} else strcat( cur_line, " ");

		if (testr[0]==0) break;
		//  .. add
		strcat( cur_line, testr );
	};
};
